#include "Statistics.h"

#include <set>
#include <list>
#include <thread>
#include "HelpFun.h"
#include "TestFun.h"
#include "SolverFun.h"
#include "DumpLog.h"
namespace Solver
{

const std::vector<char> ConsFlag{20,25,06,21};//文件存储标志

Statistics::Statistics()
{

}

Statistics::~Statistics()
{

}

bool Statistics::saveCase(const std::string &f) const
{
    //1.
    if (vecBatch_.empty())
    {
        LogInfo("当前case数为0,不需要保存");
        return false;
    }

    //2.
    File fw;
    if (!fw.openForWrite(f))
    {
        LogError("无法保存数据到文件:" + f);
        return false;
    }

    fw.writeChars(ConsFlag.data(), ConsFlag.size());

    //3.
    std::map<uint32_t, vecBaseFun_t>    mapFun;
    std::map<uint32_t, vecBaseSolver_t> mapSolver;
    std::map<uint32_t, vecCondition_t>  mapCondition;
    for (auto &b : vecBatch_)
    {
        mapFun[b->fun->getID()].push_back(b->fun);
        mapSolver[b->solver->getID()].push_back(b->solver);
        mapCondition[b->condition->id].push_back(b->condition);
    }

    //4.
    uint32_t id(0);
    fw.write<uint32_t>(mapFun.size());
    for (auto &it : mapFun)
    {
        auto &fun = it.second.front();
        fun->setID(id++);
        fun->write(fw);
    }

    //5.
    id = 0;
    fw.write<uint32_t>(mapSolver.size());
    for (auto &it : mapSolver)
    {
        auto &solver = it.second.front();
        solver->setID(id++);
        fw.write<uint16_t>(static_cast<uint16_t>(solver->getType()));
    }

    //6.
    id = 0;
    fw.write<uint32_t>(mapCondition.size());
    for (auto &it : mapCondition)
    {
        auto &c = it.second.front();
        c->id = id++;
        writeCondition(fw, *c);
    }

    //7.
    fw.write<uint32_t>(vecBatch_.size());
    for (auto &b : vecBatch_)
    {
        writeBatch(fw, b);
    }

    return true;
}

bool Statistics::loadCase(const std::string &f)
{
    //1.
    File fr;
    if (!fr.openForRead(f))
    {
        LogError("无法打开文件:" + f);
        return false;
    }
    else if(fr.getLength() < ConsFlag.size() + 16)
    {
        LogError("当前文件格式有误:" + f);
        return false;
    }

    //2.
    std::vector<char> s(ConsFlag.size());
    fr.readChars(s.data(), s.size());
    for (std::size_t i = 0; i != s.size(); ++i)
    {
        if (s[i] != ConsFlag[i])
        {
            LogError("当前文件格式有误:" + f);
            return false;
        }
    }

    //3.
    vecBaseFun_t funs;
    for (uint32_t flag = fr.read<uint32_t>(); flag; --flag)
    {
        auto fun = Test::getFun(static_cast<FunType_E>(fr.read<uint16_t>()));
        fun->read(fr);
        fun->setID(funs.size());
        funs.push_back(fun);
    }

    //4.
    vecBaseSolver_t solvers;
    for (uint32_t flag = fr.read<uint32_t>(); flag; --flag)
    {
        auto solver = SolverFun::getSolver(static_cast<SolverType_E>(fr.read<uint16_t>()));
        solver->setID(solvers.size());
        solvers.push_back(solver);
    }

    //5.
    vecCondition_t conditions;
    for (uint32_t flag = fr.read<uint32_t>(); flag; --flag)
    {
        auto c = std::make_shared<Condition_t>();
        readCondition(fr, *c);
        c->id  = conditions.size();
        conditions.push_back(c);
    }

    //6.
    vecBatch_.clear();
    for (uint32_t flag = fr.read<uint32_t>(); flag; --flag)
    {
        auto b = std::make_shared<StatBatch_t>();
        readBatch(fr, b, funs, solvers, conditions);
        vecBatch_.push_back(b);
    }

    return true;
}

void Statistics::testCase(  const vecSolverType_t &solverTypes, 
                            const vecFunType_t &funTypes,  
                            const Condition_t &con, 
                            const std::size_t dim,       //变量维度
                            const value_t minVar,        //变量下限
                            const value_t maxVar,        //变量上限
                            const uint32_t testNum ,     //函数测试次数
                            const uint16_t threadNum)
{
    //1.
    std::list<std::shared_ptr<StatBatch_t>> listBatch, listWork;
    for (uint32_t k = 0; k != testNum; ++k)
    {
        for (std::size_t i = 0; i != funTypes.size(); ++i)
        {
            for (std::size_t j = 0; j != solverTypes.size(); ++j)
            {
                auto b = createBatch(funTypes[i], solverTypes[j], con, dim, minVar, maxVar);
                b->fun->setID(i);
                b->solver->setID(j);
                b->condition->id = 0;
                listBatch.push_back(b);
            }
        }
    }

    //2.
    std::map<std::pair<FunType_E, SolverType_E>, std::shared_ptr<StatBatch_t>> mapBatch;
    while (listBatch.size())
    {
        if (listWork.size() < threadNum)
        {
            auto ptr = listBatch.front();
            ptr->solver->solve(con.popNum);
            listBatch.pop_front();
            listWork.push_back(ptr);
        }
        else
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(64));
            pushMapBatch(listWork, mapBatch);
        }
    }

    //3.
    while (listWork.size())
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(64));
        pushMapBatch(listWork, mapBatch);
    }
    
    //4.
    vecBatch_.clear();
    for (auto &it : mapBatch)
    {
        vecBatch_.push_back(it.second);
    }
}

void Statistics::readCondition(File &fr, Condition_t &con) const
{
    con.id           = fr.read<uint32_t>();
    con.popNum       = fr.read<uint32_t>();
    con.limitIter    = fr.read<uint32_t>();
    con.currentInter = fr.read<uint32_t>();
    con.limitError   = fr.read<value_t>();
    con.currentError = fr.read<value_t>();
}

void Statistics::writeCondition(File &fw, const Condition_t &con) const
{
    fw.write<uint32_t>(con.id);
    fw.write<uint32_t>(con.popNum);
    fw.write<uint32_t>(con.limitIter);
    fw.write<uint32_t>(con.currentInter);
    fw.write<value_t>(con.limitError);
    fw.write<value_t>(con.currentError);
}

void Statistics::readBatch(File &fr, std::shared_ptr<StatBatch_t> &batch, const vecBaseFun_t &funs, const vecBaseSolver_t &solvers, const vecCondition_t &conditions) const
{
    //1.
    batch->fun = funs[fr.read<uint32_t>()];

    //2.
    batch->solver = solvers[fr.read<uint32_t>()];

    //3.
    batch->condition = conditions[fr.read<uint32_t>()];

    //4.
    for (uint32_t flag = fr.read<uint32_t>(); flag; --flag)
    {
        auto c = std::make_shared<Case_t>();
        batch->vecCase.push_back(c);

        c->index    = fr.read<uint32_t>();
        c->costTime = fr.read<uint32_t>();
        c->iterNum  = fr.read<uint32_t>();
        c->dateTime = fr.read<uint64_t>();
        c->y        = fr.read<value_t>();
        Help::readVecValue(fr, c->x);
        Help::readVecValue(fr, c->history);
    }

    //5.
    updateStatBatch(batch);
}

void Statistics::writeBatch(File &fw, const std::shared_ptr<StatBatch_t> &batch) const
{
    //1.
    fw.write<uint32_t>(batch->fun->getID());

    //2.
    fw.write<uint32_t>(batch->solver->getID());

    //3.
    fw.write<uint32_t>(batch->condition->id);

    //4.
    fw.write<uint32_t>(batch->vecCase.size());
    for (auto &c : batch->vecCase)
    {
        fw.write<uint32_t>(c->index);
        fw.write<uint32_t>(c->costTime);
        fw.write<uint32_t>(c->iterNum);
        fw.write<uint64_t>(c->dateTime);
        fw.write<value_t>(c->y);
        Help::writeVecValue(fw, c->x);
        Help::writeVecValue(fw, c->history);
    }
}

void Statistics::updateStatBatch(std::shared_ptr<StatBatch_t> &batch) const
{
    //1.
    if (batch->vecCase.empty())
    {
        return;
    }

    //2. 查找最好最差case
    {
        auto it = std::minmax_element(batch->vecCase.begin(), batch->vecCase.end(), [](const std::shared_ptr<Case_t> &a, const std::shared_ptr<Case_t> &b)->bool
        {
            return a->y < b->y;
        });

        batch->bestCase  = *it.first;
        batch->worstCase = *it.second;
    }

    //3.返回中位数
    {
        auto data = batch->vecCase;
        std::nth_element(data.begin(), data.begin() + data.size() / 2, data.end(), [](const std::shared_ptr<Case_t> &a, const std::shared_ptr<Case_t> &b)->bool
        {
            return a->y < b->y;
        });

        batch->medianCase = data[data.size() / 2];
    }
    
    //4.计算均值
    batch->meanY = std::accumulate(batch->vecCase.begin(), batch->vecCase.end(), 0.0, [](value_t s, const std::shared_ptr<Case_t> &a)->value_t
    {
        return s + a->y;
    }) / batch->vecCase.size();

    //5.计算标准差
    {
        batch->stdY = 0;
        for(auto &c : batch->vecCase)
        {
            batch->stdY += std::pow(c->y - batch->meanY, 2);
        }

        batch->stdY = std::sqrt(batch->stdY / batch->vecCase.size());
    }
}

std::shared_ptr<StatBatch_t> Statistics::createBatch(FunType_E funType, SolverType_E solverType, const Condition_t &con, const std::size_t dim, const value_t minVar, const value_t maxVar) const
{
    auto b    = std::make_shared<StatBatch_t>();
    b->fun    = Test::getFun(funType, dim);
    b->solver = SolverFun::getSolver(solverType);

    b->fun->setXRange(minVar, maxVar);
    b->solver->setFun(b->fun);
    b->solver->getCondition().limitError = con.limitError;
    b->solver->getCondition().limitIter  = con.limitIter;
    b->solver->getCondition().popNum     = con.popNum;

    return b;
}

void Statistics::pushMapBatch(listStatBatch_t &listBatch, mapPairBatch_t &mapBatch) const
{
    for (auto it = listBatch.begin(); it != listBatch.end();)
    {
        //1.
        auto &b = *it;
        if (b->solver->getCondition().run.load())
        {
            ++it;
            continue;
        }

        //2.
        auto key = std::make_pair(b->fun->getType(), b->solver->getType());
        auto it2 = mapBatch.find(key);
        if (it2 != mapBatch.end())
        {
            it2->second->vecCase.push_back(createCase(b->solver));
            it2->second->vecCase.back()->index = it2->second->vecCase.size() - 1;
        }
        else
        {
            mapBatch[key] = b;
            b->vecCase.push_back(createCase(b->solver));
            b->vecCase.back()->index = b->vecCase.size() - 1;
        }

        //3.
        it = listBatch.erase(it);
    }
}

std::shared_ptr<Case_t> Statistics::createCase(std::shared_ptr<BaseSolver> &solver) const
{
    //1.
    auto c      = std::make_shared<Case_t>();
    c->y        = solver->getResult()->y;
    c->x        = solver->getResult()->x;
    c->costTime = solver->getCostTime();
    c->history  = solver->getHistory();
    c->iterNum  = solver->getCondition().currentInter;

    //2.
    std::time_t now = std::time(nullptr);
    std::tm* t      = std::localtime(&now);
    c->dateTime     = (t->tm_year + 1900) * 10000000000l + (t->tm_mon + 1) * 100000000l + t->tm_mday * 1000000l + t->tm_hour * 10000l + t->tm_min * 100l + t->tm_sec;
    
    //3.
    return c;
}

}